


/**
 * @fileoverview Global |this| required for resolving indexes in node.
 * @suppress {globalThis}
 */
(function(global) {
	'use strict';
  
	
	if (typeof module !== "undefined" && module.exports &&
	  !global["encoding-indexes"]) {
	  global["encoding-indexes"] =
		require("./encoding-indexes.js")["encoding-indexes"];
	}
  
	
	
	
  
	/**
	 * @param {number} a The number to test.
	 * @param {number} min The minimum value in the range, inclusive.
	 * @param {number} max The maximum value in the range, inclusive.
	 * @return {boolean} True if a >= min and a <= max.
	 */
	function inRange(a, min, max) {
	  return min <= a && a <= max;
	}
  
	/**
	 * @param {!Array.<*>} array The array to check.
	 * @param {*} item The item to look for in the array.
	 * @return {boolean} True if the item appears in the array.
	 */
	function includes(array, item) {
	  return array.indexOf(item) !== -1;
	}
  
	var floor = Math.floor;
  
	/**
	 * @param {*} o
	 * @return {Object}
	 */
	function ToDictionary(o) {
	  if (o === undefined) return {};
	  if (o === Object(o)) return o;
	  throw TypeError('Could not convert argument to dictionary');
	}
  
	/**
	 * @param {string} string Input string of UTF-16 code units.
	 * @return {!Array.<number>} Code points.
	 */
	function stringToCodePoints(string) {
	  
  
	  
	  var s = String(string);
  
	  
	  var n = s.length;
  
	  
	  var i = 0;
  
	  
	  var u = [];
  
	  
	  while (i < n) {
  
		
		var c = s.charCodeAt(i);
  
		
  
		
		if (c < 0xD800 || c > 0xDFFF) {
		  
		  u.push(c);
		}
  
		
		else if (0xDC00 <= c && c <= 0xDFFF) {
		  
		  u.push(0xFFFD);
		}
  
		
		else if (0xD800 <= c && c <= 0xDBFF) {
		  
		  
		  if (i === n - 1) {
			u.push(0xFFFD);
		  }
		  
		  else {
			
			var d = s.charCodeAt(i + 1);
  
			
			if (0xDC00 <= d && d <= 0xDFFF) {
			  
			  var a = c & 0x3FF;
  
			  
			  var b = d & 0x3FF;
  
			  
			  
			  u.push(0x10000 + (a << 10) + b);
  
			  
			  i += 1;
			}
  
			
			
			else  {
			  u.push(0xFFFD);
			}
		  }
		}
  
		
		i += 1;
	  }
  
	  
	  return u;
	}
  
	/**
	 * @param {!Array.<number>} code_points Array of code points.
	 * @return {string} string String of UTF-16 code units.
	 */
	function codePointsToString(code_points) {
	  var s = '';
	  for (var i = 0; i < code_points.length; ++i) {
		var cp = code_points[i];
		if (cp <= 0xFFFF) {
		  s += String.fromCharCode(cp);
		} else {
		  cp -= 0x10000;
		  s += String.fromCharCode((cp >> 10) + 0xD800,
								   (cp & 0x3FF) + 0xDC00);
		}
	  }
	  return s;
	}
  
  
	
	
	
	
  
	
	
	
  
	/**
	 * An ASCII byte is a byte in the range 0x00 to 0x7F, inclusive.
	 * @param {number} a The number to test.
	 * @return {boolean} True if a is in the range 0x00 to 0x7F, inclusive.
	 */
	function isASCIIByte(a) {
	  return 0x00 <= a && a <= 0x7F;
	}
  
	/**
	 * An ASCII code point is a code point in the range U+0000 to
	 * U+007F, inclusive.
	 */
	var isASCIICodePoint = isASCIIByte;
  
  
	/**
	 * End-of-stream is a special token that signifies no more tokens
	 * are in the stream.
	 * @const
	 */ var end_of_stream = -1;
  
	/**
	 * A stream represents an ordered sequence of tokens.
	 *
	 * @constructor
	 * @param {!(Array.<number>|Uint8Array)} tokens Array of tokens that provide
	 * the stream.
	 */
	function Stream(tokens) {
	  /** @type {!Array.<number>} */
	  this.tokens = [].slice.call(tokens);
	  
	  this.tokens.reverse();
	}
  
	Stream.prototype = {
	  /**
	   * @return {boolean} True if end-of-stream has been hit.
	   */
	  endOfStream: function() {
		return !this.tokens.length;
	  },
  
	  /**
	   * When a token is read from a stream, the first token in the
	   * stream must be returned and subsequently removed, and
	   * end-of-stream must be returned otherwise.
	   *
	   * @return {number} Get the next token from the stream, or
	   * end_of_stream.
	   */
	   read: function() {
		if (!this.tokens.length)
		  return end_of_stream;
		 return this.tokens.pop();
	   },
  
	  /**
	   * When one or more tokens are prepended to a stream, those tokens
	   * must be inserted, in given order, before the first token in the
	   * stream.
	   *
	   * @param {(number|!Array.<number>)} token The token(s) to prepend to the
	   * stream.
	   */
	  prepend: function(token) {
		if (Array.isArray(token)) {
		  var tokens = /**@type {!Array.<number>}*/(token);
		  while (tokens.length)
			this.tokens.push(tokens.pop());
		} else {
		  this.tokens.push(token);
		}
	  },
  
	  /**
	   * When one or more tokens are pushed to a stream, those tokens
	   * must be inserted, in given order, after the last token in the
	   * stream.
	   *
	   * @param {(number|!Array.<number>)} token The tokens(s) to push to the
	   * stream.
	   */
	  push: function(token) {
		if (Array.isArray(token)) {
		  var tokens = /**@type {!Array.<number>}*/(token);
		  while (tokens.length)
			this.tokens.unshift(tokens.shift());
		} else {
		  this.tokens.unshift(token);
		}
	  }
	};
  
	
	
	
  
	
  
	/** @const */
	var finished = -1;
  
	/**
	 * @param {boolean} fatal If true, decoding errors raise an exception.
	 * @param {number=} opt_code_point Override the standard fallback code point.
	 * @return {number} The code point to insert on a decoding error.
	 */
	function decoderError(fatal, opt_code_point) {
	  if (fatal)
		throw TypeError('Decoder error');
	  return opt_code_point || 0xFFFD;
	}
  
	/**
	 * @param {number} code_point The code point that could not be encoded.
	 * @return {number} Always throws, no value is actually returned.
	 */
	function encoderError(code_point) {
	  throw TypeError('The code point ' + code_point + ' could not be encoded.');
	}
  
	/** @interface */
	function Decoder() {}
	Decoder.prototype = {
	  /**
	   * @param {Stream} stream The stream of bytes being decoded.
	   * @param {number} bite The next byte read from the stream.
	   * @return {?(number|!Array.<number>)} The next code point(s)
	   *     decoded, or null if not enough data exists in the input
	   *     stream to decode a complete code point, or |finished|.
	   */
	  handler: function(stream, bite) {}
	};
  
	/** @interface */
	function Encoder() {}
	Encoder.prototype = {
	  /**
	   * @param {Stream} stream The stream of code points being encoded.
	   * @param {number} code_point Next code point read from the stream.
	   * @return {(number|!Array.<number>)} Byte(s) to emit, or |finished|.
	   */
	  handler: function(stream, code_point) {}
	};
  
	
  
	
	
  
	/**
	 * @param {string} label The encoding label.
	 * @return {?{name:string,labels:Array.<string>}}
	 */
	function getEncoding(label) {
	  
	  label = String(label).trim().toLowerCase();
  
	  
	  
	  
	  if (Object.prototype.hasOwnProperty.call(label_to_encoding, label)) {
		return label_to_encoding[label];
	  }
	  return null;
	}
  
	/**
	 * Encodings table: https:
	 * @const
	 * @type {!Array.<{
	 *          heading: string,
	 *          encodings: Array.<{name:string,labels:Array.<string>}>
	 *        }>}
	 */
	var encodings = [
	  {
		"encodings": [
		  {
			"labels": [
			  "unicode-1-1-utf-8",
			  "utf-8",
			  "utf8"
			],
			"name": "UTF-8"
		  }
		],
		"heading": "The Encoding"
	  },
	  {
		"encodings": [
		  {
			"labels": [
			  "866",
			  "cp866",
			  "csibm866",
			  "ibm866"
			],
			"name": "IBM866"
		  },
		  {
			"labels": [
			  "csisolatin2",
			  "iso-8859-2",
			  "iso-ir-101",
			  "iso8859-2",
			  "iso88592",
			  "iso_8859-2",
			  "iso_8859-2:1987",
			  "l2",
			  "latin2"
			],
			"name": "ISO-8859-2"
		  },
		  {
			"labels": [
			  "csisolatin3",
			  "iso-8859-3",
			  "iso-ir-109",
			  "iso8859-3",
			  "iso88593",
			  "iso_8859-3",
			  "iso_8859-3:1988",
			  "l3",
			  "latin3"
			],
			"name": "ISO-8859-3"
		  },
		  {
			"labels": [
			  "csisolatin4",
			  "iso-8859-4",
			  "iso-ir-110",
			  "iso8859-4",
			  "iso88594",
			  "iso_8859-4",
			  "iso_8859-4:1988",
			  "l4",
			  "latin4"
			],
			"name": "ISO-8859-4"
		  },
		  {
			"labels": [
			  "csisolatincyrillic",
			  "cyrillic",
			  "iso-8859-5",
			  "iso-ir-144",
			  "iso8859-5",
			  "iso88595",
			  "iso_8859-5",
			  "iso_8859-5:1988"
			],
			"name": "ISO-8859-5"
		  },
		  {
			"labels": [
			  "arabic",
			  "asmo-708",
			  "csiso88596e",
			  "csiso88596i",
			  "csisolatinarabic",
			  "ecma-114",
			  "iso-8859-6",
			  "iso-8859-6-e",
			  "iso-8859-6-i",
			  "iso-ir-127",
			  "iso8859-6",
			  "iso88596",
			  "iso_8859-6",
			  "iso_8859-6:1987"
			],
			"name": "ISO-8859-6"
		  },
		  {
			"labels": [
			  "csisolatingreek",
			  "ecma-118",
			  "elot_928",
			  "greek",
			  "greek8",
			  "iso-8859-7",
			  "iso-ir-126",
			  "iso8859-7",
			  "iso88597",
			  "iso_8859-7",
			  "iso_8859-7:1987",
			  "sun_eu_greek"
			],
			"name": "ISO-8859-7"
		  },
		  {
			"labels": [
			  "csiso88598e",
			  "csisolatinhebrew",
			  "hebrew",
			  "iso-8859-8",
			  "iso-8859-8-e",
			  "iso-ir-138",
			  "iso8859-8",
			  "iso88598",
			  "iso_8859-8",
			  "iso_8859-8:1988",
			  "visual"
			],
			"name": "ISO-8859-8"
		  },
		  {
			"labels": [
			  "csiso88598i",
			  "iso-8859-8-i",
			  "logical"
			],
			"name": "ISO-8859-8-I"
		  },
		  {
			"labels": [
			  "csisolatin6",
			  "iso-8859-10",
			  "iso-ir-157",
			  "iso8859-10",
			  "iso885910",
			  "l6",
			  "latin6"
			],
			"name": "ISO-8859-10"
		  },
		  {
			"labels": [
			  "iso-8859-13",
			  "iso8859-13",
			  "iso885913"
			],
			"name": "ISO-8859-13"
		  },
		  {
			"labels": [
			  "iso-8859-14",
			  "iso8859-14",
			  "iso885914"
			],
			"name": "ISO-8859-14"
		  },
		  {
			"labels": [
			  "csisolatin9",
			  "iso-8859-15",
			  "iso8859-15",
			  "iso885915",
			  "iso_8859-15",
			  "l9"
			],
			"name": "ISO-8859-15"
		  },
		  {
			"labels": [
			  "iso-8859-16"
			],
			"name": "ISO-8859-16"
		  },
		  {
			"labels": [
			  "cskoi8r",
			  "koi",
			  "koi8",
			  "koi8-r",
			  "koi8_r"
			],
			"name": "KOI8-R"
		  },
		  {
			"labels": [
			  "koi8-ru",
			  "koi8-u"
			],
			"name": "KOI8-U"
		  },
		  {
			"labels": [
			  "csmacintosh",
			  "mac",
			  "macintosh",
			  "x-mac-roman"
			],
			"name": "macintosh"
		  },
		  {
			"labels": [
			  "dos-874",
			  "iso-8859-11",
			  "iso8859-11",
			  "iso885911",
			  "tis-620",
			  "windows-874"
			],
			"name": "windows-874"
		  },
		  {
			"labels": [
			  "cp1250",
			  "windows-1250",
			  "x-cp1250"
			],
			"name": "windows-1250"
		  },
		  {
			"labels": [
			  "cp1251",
			  "windows-1251",
			  "x-cp1251"
			],
			"name": "windows-1251"
		  },
		  {
			"labels": [
			  "ansi_x3.4-1968",
			  "ascii",
			  "cp1252",
			  "cp819",
			  "csisolatin1",
			  "ibm819",
			  "iso-8859-1",
			  "iso-ir-100",
			  "iso8859-1",
			  "iso88591",
			  "iso_8859-1",
			  "iso_8859-1:1987",
			  "l1",
			  "latin1",
			  "us-ascii",
			  "windows-1252",
			  "x-cp1252"
			],
			"name": "windows-1252"
		  },
		  {
			"labels": [
			  "cp1253",
			  "windows-1253",
			  "x-cp1253"
			],
			"name": "windows-1253"
		  },
		  {
			"labels": [
			  "cp1254",
			  "csisolatin5",
			  "iso-8859-9",
			  "iso-ir-148",
			  "iso8859-9",
			  "iso88599",
			  "iso_8859-9",
			  "iso_8859-9:1989",
			  "l5",
			  "latin5",
			  "windows-1254",
			  "x-cp1254"
			],
			"name": "windows-1254"
		  },
		  {
			"labels": [
			  "cp1255",
			  "windows-1255",
			  "x-cp1255"
			],
			"name": "windows-1255"
		  },
		  {
			"labels": [
			  "cp1256",
			  "windows-1256",
			  "x-cp1256"
			],
			"name": "windows-1256"
		  },
		  {
			"labels": [
			  "cp1257",
			  "windows-1257",
			  "x-cp1257"
			],
			"name": "windows-1257"
		  },
		  {
			"labels": [
			  "cp1258",
			  "windows-1258",
			  "x-cp1258"
			],
			"name": "windows-1258"
		  },
		  {
			"labels": [
			  "x-mac-cyrillic",
			  "x-mac-ukrainian"
			],
			"name": "x-mac-cyrillic"
		  }
		],
		"heading": "Legacy single-byte encodings"
	  },
	  {
		"encodings": [
		  {
			"labels": [
			  "chinese",
			  "csgb2312",
			  "csiso58gb231280",
			  "gb2312",
			  "gb_2312",
			  "gb_2312-80",
			  "gbk",
			  "iso-ir-58",
			  "x-gbk"
			],
			"name": "GBK"
		  },
		  {
			"labels": [
			  "gb18030"
			],
			"name": "gb18030"
		  }
		],
		"heading": "Legacy multi-byte Chinese (simplified) encodings"
	  },
	  {
		"encodings": [
		  {
			"labels": [
			  "big5",
			  "big5-hkscs",
			  "cn-big5",
			  "csbig5",
			  "x-x-big5"
			],
			"name": "Big5"
		  }
		],
		"heading": "Legacy multi-byte Chinese (traditional) encodings"
	  },
	  {
		"encodings": [
		  {
			"labels": [
			  "cseucpkdfmtjapanese",
			  "euc-jp",
			  "x-euc-jp"
			],
			"name": "EUC-JP"
		  },
		  {
			"labels": [
			  "csiso2022jp",
			  "iso-2022-jp"
			],
			"name": "ISO-2022-JP"
		  },
		  {
			"labels": [
			  "csshiftjis",
			  "ms932",
			  "ms_kanji",
			  "shift-jis",
			  "shift_jis",
			  "sjis",
			  "windows-31j",
			  "x-sjis"
			],
			"name": "Shift_JIS"
		  }
		],
		"heading": "Legacy multi-byte Japanese encodings"
	  },
	  {
		"encodings": [
		  {
			"labels": [
			  "cseuckr",
			  "csksc56011987",
			  "euc-kr",
			  "iso-ir-149",
			  "korean",
			  "ks_c_5601-1987",
			  "ks_c_5601-1989",
			  "ksc5601",
			  "ksc_5601",
			  "windows-949"
			],
			"name": "EUC-KR"
		  }
		],
		"heading": "Legacy multi-byte Korean encodings"
	  },
	  {
		"encodings": [
		  {
			"labels": [
			  "csiso2022kr",
			  "hz-gb-2312",
			  "iso-2022-cn",
			  "iso-2022-cn-ext",
			  "iso-2022-kr"
			],
			"name": "replacement"
		  },
		  {
			"labels": [
			  "utf-16be"
			],
			"name": "UTF-16BE"
		  },
		  {
			"labels": [
			  "utf-16",
			  "utf-16le"
			],
			"name": "UTF-16LE"
		  },
		  {
			"labels": [
			  "x-user-defined"
			],
			"name": "x-user-defined"
		  }
		],
		"heading": "Legacy miscellaneous encodings"
	  }
	];
  
	
	/** @type {Object.<string,{name:string,labels:Array.<string>}>} */
	var label_to_encoding = {};
	encodings.forEach(function(category) {
	  category.encodings.forEach(function(encoding) {
		encoding.labels.forEach(function(label) {
		  label_to_encoding[label] = encoding;
		});
	  });
	});
  
	
	/** @type {Object.<string, function({fatal:boolean}): Encoder>} */
	var encoders = {};
	/** @type {Object.<string, function({fatal:boolean}): Decoder>} */
	var decoders = {};
  
	
	
	
  
	/**
	 * @param {number} pointer The |pointer| to search for.
	 * @param {(!Array.<?number>|undefined)} index The |index| to search within.
	 * @return {?number} The code point corresponding to |pointer| in |index|,
	 *     or null if |code point| is not in |index|.
	 */
	function indexCodePointFor(pointer, index) {
	  if (!index) return null;
	  return index[pointer] || null;
	}
  
	/**
	 * @param {number} code_point The |code point| to search for.
	 * @param {!Array.<?number>} index The |index| to search within.
	 * @return {?number} The first pointer corresponding to |code point| in
	 *     |index|, or null if |code point| is not in |index|.
	 */
	function indexPointerFor(code_point, index) {
	  var pointer = index.indexOf(code_point);
	  return pointer === -1 ? null : pointer;
	}
  
	/**
	 * @param {string} name Name of the index.
	 * @return {(!Array.<number>|!Array.<Array.<number>>)}
	 *  */
	function index(name) {
	  if (!('encoding-indexes' in global)) {
		throw Error("Indexes missing." +
					" Did you forget to include encoding-indexes.js first?");
	  }
	  return global['encoding-indexes'][name];
	}
  
	/**
	 * @param {number} pointer The |pointer| to search for in the gb18030 index.
	 * @return {?number} The code point corresponding to |pointer| in |index|,
	 *     or null if |code point| is not in the gb18030 index.
	 */
	function indexGB18030RangesCodePointFor(pointer) {
	  
	  
	  if ((pointer > 39419 && pointer < 189000) || (pointer > 1237575))
		return null;
  
	  
	  if (pointer === 7457) return 0xE7C7;
  
	  
	  
	  
	  var offset = 0;
	  var code_point_offset = 0;
	  var idx = index('gb18030-ranges');
	  var i;
	  for (i = 0; i < idx.length; ++i) {
		/** @type {!Array.<number>} */
		var entry = idx[i];
		if (entry[0] <= pointer) {
		  offset = entry[0];
		  code_point_offset = entry[1];
		} else {
		  break;
		}
	  }
  
	  
	  
	  return code_point_offset + pointer - offset;
	}
  
	/**
	 * @param {number} code_point The |code point| to locate in the gb18030 index.
	 * @return {number} The first pointer corresponding to |code point| in the
	 *     gb18030 index.
	 */
	function indexGB18030RangesPointerFor(code_point) {
	  
	  if (code_point === 0xE7C7) return 7457;
  
	  
	  
	  
	  var offset = 0;
	  var pointer_offset = 0;
	  var idx = index('gb18030-ranges');
	  var i;
	  for (i = 0; i < idx.length; ++i) {
		/** @type {!Array.<number>} */
		var entry = idx[i];
		if (entry[1] <= code_point) {
		  offset = entry[1];
		  pointer_offset = entry[0];
		} else {
		  break;
		}
	  }
  
	  
	  
	  return pointer_offset + code_point - offset;
	}
  
	/**
	 * @param {number} code_point The |code_point| to search for in the Shift_JIS
	 *     index.
	 * @return {?number} The code point corresponding to |pointer| in |index|,
	 *     or null if |code point| is not in the Shift_JIS index.
	 */
	function indexShiftJISPointerFor(code_point) {
	  
	  
	  shift_jis_index = shift_jis_index ||
		index('jis0208').map(function(code_point, pointer) {
		  return inRange(pointer, 8272, 8835) ? null : code_point;
		});
	  var index_ = shift_jis_index;
  
	  
	  return index_.indexOf(code_point);
	}
	var shift_jis_index;
  
	/**
	 * @param {number} code_point The |code_point| to search for in the big5
	 *     index.
	 * @return {?number} The code point corresponding to |pointer| in |index|,
	 *     or null if |code point| is not in the big5 index.
	 */
	function indexBig5PointerFor(code_point) {
	  
	  big5_index_no_hkscs = big5_index_no_hkscs ||
		index('big5').map(function(code_point, pointer) {
		  return (pointer < (0xA1 - 0x81) * 157) ? null : code_point;
		});
	  var index_ = big5_index_no_hkscs;
  
	  
	  
	  
	  if (code_point === 0x2550 || code_point === 0x255E ||
		  code_point === 0x2561 || code_point === 0x256A ||
		  code_point === 0x5341 || code_point === 0x5345) {
		return index_.lastIndexOf(code_point);
	  }
  
	  
	  return indexPointerFor(code_point, index_);
	}
	var big5_index_no_hkscs;
  
	
	
	
  
	/** @const */ var DEFAULT_ENCODING = 'utf-8';
  
	
  
	/**
	 * @constructor
	 * @param {string=} label The label of the encoding;
	 *     defaults to 'utf-8'.
	 * @param {Object=} options
	 */
	function TextDecoder(label, options) {
	  
	  if (!(this instanceof TextDecoder))
		throw TypeError('Called as a function. Did you forget \'new\'?');
	  label = label !== undefined ? String(label) : DEFAULT_ENCODING;
	  options = ToDictionary(options);
  
	  
	  
	  
	  
  
	  /** @private */
	  this._encoding = null;
	  /** @private @type {?Decoder} */
	  this._decoder = null;
	  /** @private @type {boolean} */
	  this._ignoreBOM = false;
	  /** @private @type {boolean} */
	  this._BOMseen = false;
	  /** @private @type {string} */
	  this._error_mode = 'replacement';
	  /** @private @type {boolean} */
	  this._do_not_flush = false;
  
  
	  
	  
	  var encoding = getEncoding(label);
  
	  
	  if (encoding === null || encoding.name === 'replacement')
		throw RangeError('Unknown encoding: ' + label);
	  if (!decoders[encoding.name]) {
		throw Error('Decoder not present.' +
					' Did you forget to include encoding-indexes.js first?');
	  }
  
	  
	  var dec = this;
  
	  
	  dec._encoding = encoding;
  
	  
	  
	  if (Boolean(options['fatal']))
		dec._error_mode = 'fatal';
  
	  
	  
	  if (Boolean(options['ignoreBOM']))
		dec._ignoreBOM = true;
  
	  
	  if (!Object.defineProperty) {
		this.encoding = dec._encoding.name.toLowerCase();
		this.fatal = dec._error_mode === 'fatal';
		this.ignoreBOM = dec._ignoreBOM;
	  }
  
	  
	  return dec;
	}
  
	if (Object.defineProperty) {
	  
	  Object.defineProperty(TextDecoder.prototype, 'encoding', {
		/** @this {TextDecoder} */
		get: function() { return this._encoding.name.toLowerCase(); }
	  });
  
	  
	  
	  Object.defineProperty(TextDecoder.prototype, 'fatal', {
		/** @this {TextDecoder} */
		get: function() { return this._error_mode === 'fatal'; }
	  });
  
	  
	  
	  Object.defineProperty(TextDecoder.prototype, 'ignoreBOM', {
		/** @this {TextDecoder} */
		get: function() { return this._ignoreBOM; }
	  });
	}
  
	/**
	 * @param {BufferSource=} input The buffer of bytes to decode.
	 * @param {Object=} options
	 * @return {string} The decoded string.
	 */
	TextDecoder.prototype.decode = function decode(input, options) {
	  var bytes;
	  if (typeof input === 'object' && input instanceof ArrayBuffer) {
		bytes = new Uint8Array(input);
	  } else if (typeof input === 'object' && 'buffer' in input &&
				 input.buffer instanceof ArrayBuffer) {
		bytes = new Uint8Array(input.buffer,
							   input.byteOffset,
							   input.byteLength);
	  } else {
		bytes = new Uint8Array(0);
	  }
  
	  options = ToDictionary(options);
  
	  
	  
	  
	  if (!this._do_not_flush) {
		this._decoder = decoders[this._encoding.name]({
		  fatal: this._error_mode === 'fatal'});
		this._BOMseen = false;
	  }
  
	  
	  
	  this._do_not_flush = Boolean(options['stream']);
  
	  
	  
	  var input_stream = new Stream(bytes);
  
	  
	  var output = [];
  
	  /** @type {?(number|!Array.<number>)} */
	  var result;
  
	  
	  while (true) {
		
		var token = input_stream.read();
  
		
		
		
		if (token === end_of_stream)
		  break;
  
		
  
		
		
		result = this._decoder.handler(input_stream, token);
  
		
		if (result === finished)
		  break;
  
		if (result !== null) {
		  if (Array.isArray(result))
			output.push.apply(output, /**@type {!Array.<number>}*/(result));
		  else
			output.push(result);
		}
  
		
		
  
		
	  }
	  
	  if (!this._do_not_flush) {
		do {
		  result = this._decoder.handler(input_stream, input_stream.read());
		  if (result === finished)
			break;
		  if (result === null)
			continue;
		  if (Array.isArray(result))
			output.push.apply(output, /**@type {!Array.<number>}*/(result));
		  else
			output.push(result);
		} while (!input_stream.endOfStream());
		this._decoder = null;
	  }
  
	  
	  
	  /**
	   * @param {!Array.<number>} stream
	   * @return {string}
	   * @this {TextDecoder}
	   */
	  function serializeStream(stream) {
		
		
  
		
		
		if (includes(['UTF-8', 'UTF-16LE', 'UTF-16BE'], this._encoding.name) &&
			!this._ignoreBOM && !this._BOMseen) {
		  if (stream.length > 0 && stream[0] === 0xFEFF) {
			
			this._BOMseen = true;
			stream.shift();
		  } else if (stream.length > 0) {
			
			
			this._BOMseen = true;
		  } else {
			
			
			
		  }
		}
		
		return codePointsToString(stream);
	  }
  
	  return serializeStream.call(this, output);
	};
  
	
  
	/**
	 * @constructor
	 * @param {string=} label The label of the encoding. NONSTANDARD.
	 * @param {Object=} options NONSTANDARD.
	 */
	function TextEncoder(label, options) {
	  
	  if (!(this instanceof TextEncoder))
		throw TypeError('Called as a function. Did you forget \'new\'?');
	  options = ToDictionary(options);
  
	  
  
	  /** @private */
	  this._encoding = null;
	  /** @private @type {?Encoder} */
	  this._encoder = null;
  
	  
	  /** @private @type {boolean} */
	  this._do_not_flush = false;
	  /** @private @type {string} */
	  this._fatal = Boolean(options['fatal']) ? 'fatal' : 'replacement';
  
	  
	  var enc = this;
  
	  
	  if (Boolean(options['NONSTANDARD_allowLegacyEncoding'])) {
		
		label = label !== undefined ? String(label) : DEFAULT_ENCODING;
		var encoding = getEncoding(label);
		if (encoding === null || encoding.name === 'replacement')
		  throw RangeError('Unknown encoding: ' + label);
		if (!encoders[encoding.name]) {
		  throw Error('Encoder not present.' +
					  ' Did you forget to include encoding-indexes.js first?');
		}
		enc._encoding = encoding;
	  } else {
		
		enc._encoding = getEncoding('utf-8');
  
		if (label !== undefined && 'console' in global) {
		  console.warn('TextEncoder constructor called with encoding label, '
					   + 'which is ignored.');
		}
	  }
  
	  
	  if (!Object.defineProperty)
		this.encoding = enc._encoding.name.toLowerCase();
  
	  
	  return enc;
	}
  
	if (Object.defineProperty) {
	  
	  Object.defineProperty(TextEncoder.prototype, 'encoding', {
		/** @this {TextEncoder} */
		get: function() { return this._encoding.name.toLowerCase(); }
	  });
	}
  
	/**
	 * @param {string=} opt_string The string to encode.
	 * @param {Object=} options
	 * @return {!Uint8Array} Encoded bytes, as a Uint8Array.
	 */
	TextEncoder.prototype.encode = function encode(opt_string, options) {
	  opt_string = opt_string === undefined ? '' : String(opt_string);
	  options = ToDictionary(options);
  
	  
	  
	  
	  if (!this._do_not_flush)
		this._encoder = encoders[this._encoding.name]({
		  fatal: this._fatal === 'fatal'});
	  this._do_not_flush = Boolean(options['stream']);
  
	  
	  var input = new Stream(stringToCodePoints(opt_string));
  
	  
	  var output = [];
  
	  /** @type {?(number|!Array.<number>)} */
	  var result;
	  
	  while (true) {
		
		var token = input.read();
		if (token === end_of_stream)
		  break;
		
		
		result = this._encoder.handler(input, token);
		if (result === finished)
		  break;
		if (Array.isArray(result))
		  output.push.apply(output, /**@type {!Array.<number>}*/(result));
		else
		  output.push(result);
	  }
	  
	  if (!this._do_not_flush) {
		while (true) {
		  result = this._encoder.handler(input, input.read());
		  if (result === finished)
			break;
		  if (Array.isArray(result))
			output.push.apply(output, /**@type {!Array.<number>}*/(result));
		  else
			output.push(result);
		}
		this._encoder = null;
	  }
	  
	  
	  
	  return new Uint8Array(output);
	};
  
  
	
	
	
  
	
  
	
	/**
	 * @constructor
	 * @implements {Decoder}
	 * @param {{fatal: boolean}} options
	 */
	function UTF8Decoder(options) {
	  var fatal = options.fatal;
  
	  
	  
	  
	  
	  var /** @type {number} */ utf8_code_point = 0,
		  /** @type {number} */ utf8_bytes_seen = 0,
		  /** @type {number} */ utf8_bytes_needed = 0,
		  /** @type {number} */ utf8_lower_boundary = 0x80,
		  /** @type {number} */ utf8_upper_boundary = 0xBF;
  
	  /**
	   * @param {Stream} stream The stream of bytes being decoded.
	   * @param {number} bite The next byte read from the stream.
	   * @return {?(number|!Array.<number>)} The next code point(s)
	   *     decoded, or null if not enough data exists in the input
	   *     stream to decode a complete code point.
	   */
	  this.handler = function(stream, bite) {
		
		
		if (bite === end_of_stream && utf8_bytes_needed !== 0) {
		  utf8_bytes_needed = 0;
		  return decoderError(fatal);
		}
  
		
		if (bite === end_of_stream)
		  return finished;
  
		
		if (utf8_bytes_needed === 0) {
  
		  
		  if (inRange(bite, 0x00, 0x7F)) {
			
			return bite;
		  }
  
		  
		  else if (inRange(bite, 0xC2, 0xDF)) {
			
			utf8_bytes_needed = 1;
  
			
			utf8_code_point = bite & 0x1F;
		  }
  
		  
		  else if (inRange(bite, 0xE0, 0xEF)) {
			
			if (bite === 0xE0)
			  utf8_lower_boundary = 0xA0;
			
			if (bite === 0xED)
			  utf8_upper_boundary = 0x9F;
			
			utf8_bytes_needed = 2;
			
			utf8_code_point = bite & 0xF;
		  }
  
		  
		  else if (inRange(bite, 0xF0, 0xF4)) {
			
			if (bite === 0xF0)
			  utf8_lower_boundary = 0x90;
			
			if (bite === 0xF4)
			  utf8_upper_boundary = 0x8F;
			
			utf8_bytes_needed = 3;
			
			utf8_code_point = bite & 0x7;
		  }
  
		  
		  else {
			
			return decoderError(fatal);
		  }
  
		  
		  return null;
		}
  
		
		
		if (!inRange(bite, utf8_lower_boundary, utf8_upper_boundary)) {
  
		  
		  
		  
		  utf8_code_point = utf8_bytes_needed = utf8_bytes_seen = 0;
		  utf8_lower_boundary = 0x80;
		  utf8_upper_boundary = 0xBF;
  
		  
		  stream.prepend(bite);
  
		  
		  return decoderError(fatal);
		}
  
		
		
		utf8_lower_boundary = 0x80;
		utf8_upper_boundary = 0xBF;
  
		
		
		utf8_code_point = (utf8_code_point << 6) | (bite & 0x3F);
  
		
		utf8_bytes_seen += 1;
  
		
		
		if (utf8_bytes_seen !== utf8_bytes_needed)
		  return null;
  
		
		var code_point = utf8_code_point;
  
		
		
		utf8_code_point = utf8_bytes_needed = utf8_bytes_seen = 0;
  
		
		return code_point;
	  };
	}
  
	
	/**
	 * @constructor
	 * @implements {Encoder}
	 * @param {{fatal: boolean}} options
	 */
	function UTF8Encoder(options) {
	  var fatal = options.fatal;
	  /**
	   * @param {Stream} stream Input stream.
	   * @param {number} code_point Next code point read from the stream.
	   * @return {(number|!Array.<number>)} Byte(s) to emit.
	   */
	  this.handler = function(stream, code_point) {
		
		if (code_point === end_of_stream)
		  return finished;
  
		
		
		if (isASCIICodePoint(code_point))
		  return code_point;
  
		
		var count, offset;
		
		if (inRange(code_point, 0x0080, 0x07FF)) {
		  
		  count = 1;
		  offset = 0xC0;
		}
		
		else if (inRange(code_point, 0x0800, 0xFFFF)) {
		  
		  count = 2;
		  offset = 0xE0;
		}
		
		else if (inRange(code_point, 0x10000, 0x10FFFF)) {
		  
		  count = 3;
		  offset = 0xF0;
		}
  
		
		
		var bytes = [(code_point >> (6 * count)) + offset];
  
		
		while (count > 0) {
  
		  
		  var temp = code_point >> (6 * (count - 1));
  
		  
		  bytes.push(0x80 | (temp & 0x3F));
  
		  
		  count -= 1;
		}
  
		
		return bytes;
	  };
	}
  
	/** @param {{fatal: boolean}} options */
	encoders['UTF-8'] = function(options) {
	  return new UTF8Encoder(options);
	};
	/** @param {{fatal: boolean}} options */
	decoders['UTF-8'] = function(options) {
	  return new UTF8Decoder(options);
	};
  
	
	
	
  
	
	/**
	 * @constructor
	 * @implements {Decoder}
	 * @param {!Array.<number>} index The encoding index.
	 * @param {{fatal: boolean}} options
	 */
	function SingleByteDecoder(index, options) {
	  var fatal = options.fatal;
	  /**
	   * @param {Stream} stream The stream of bytes being decoded.
	   * @param {number} bite The next byte read from the stream.
	   * @return {?(number|!Array.<number>)} The next code point(s)
	   *     decoded, or null if not enough data exists in the input
	   *     stream to decode a complete code point.
	   */
	  this.handler = function(stream, bite) {
		
		if (bite === end_of_stream)
		  return finished;
  
		
		
		if (isASCIIByte(bite))
		  return bite;
  
		
		
		var code_point = index[bite - 0x80];
  
		
		if (code_point === null)
		  return decoderError(fatal);
  
		
		return code_point;
	  };
	}
  
	
	/**
	 * @constructor
	 * @implements {Encoder}
	 * @param {!Array.<?number>} index The encoding index.
	 * @param {{fatal: boolean}} options
	 */
	function SingleByteEncoder(index, options) {
	  var fatal = options.fatal;
	  /**
	   * @param {Stream} stream Input stream.
	   * @param {number} code_point Next code point read from the stream.
	   * @return {(number|!Array.<number>)} Byte(s) to emit.
	   */
	  this.handler = function(stream, code_point) {
		
		if (code_point === end_of_stream)
		  return finished;
  
		
		
		if (isASCIICodePoint(code_point))
		  return code_point;
  
		
		
		var pointer = indexPointerFor(code_point, index);
  
		
		if (pointer === null)
		  encoderError(code_point);
  
		
		return pointer + 0x80;
	  };
	}
  
	(function() {
	  if (!('encoding-indexes' in global))
		return;
	  encodings.forEach(function(category) {
		if (category.heading !== 'Legacy single-byte encodings')
		  return;
		category.encodings.forEach(function(encoding) {
		  var name = encoding.name;
		  var idx = index(name.toLowerCase());
		  /** @param {{fatal: boolean}} options */
		  decoders[name] = function(options) {
			return new SingleByteDecoder(idx, options);
		  };
		  /** @param {{fatal: boolean}} options */
		  encoders[name] = function(options) {
			return new SingleByteEncoder(idx, options);
		  };
		});
	  });
	}());
  
	
	
	
  
	
  
	
	
	/** @param {{fatal: boolean}} options */
	decoders['GBK'] = function(options) {
	  return new GB18030Decoder(options);
	};
  
	
	
	/** @param {{fatal: boolean}} options */
	encoders['GBK'] = function(options) {
	  return new GB18030Encoder(options, true);
	};
  
	
  
	
	/**
	 * @constructor
	 * @implements {Decoder}
	 * @param {{fatal: boolean}} options
	 */
	function GB18030Decoder(options) {
	  var fatal = options.fatal;
	  
	  
	  var /** @type {number} */ gb18030_first = 0x00,
		  /** @type {number} */ gb18030_second = 0x00,
		  /** @type {number} */ gb18030_third = 0x00;
	  /**
	   * @param {Stream} stream The stream of bytes being decoded.
	   * @param {number} bite The next byte read from the stream.
	   * @return {?(number|!Array.<number>)} The next code point(s)
	   *     decoded, or null if not enough data exists in the input
	   *     stream to decode a complete code point.
	   */
	  this.handler = function(stream, bite) {
		
		
		if (bite === end_of_stream && gb18030_first === 0x00 &&
			gb18030_second === 0x00 && gb18030_third === 0x00) {
		  return finished;
		}
		
		
		
		if (bite === end_of_stream &&
			(gb18030_first !== 0x00 || gb18030_second !== 0x00 ||
			 gb18030_third !== 0x00)) {
		  gb18030_first = 0x00;
		  gb18030_second = 0x00;
		  gb18030_third = 0x00;
		  decoderError(fatal);
		}
		var code_point;
		
		if (gb18030_third !== 0x00) {
		  
		  code_point = null;
		  
		  
		  
		  
		  if (inRange(bite, 0x30, 0x39)) {
			code_point = indexGB18030RangesCodePointFor(
				(((gb18030_first - 0x81) * 10 + gb18030_second - 0x30) * 126 +
				 gb18030_third - 0x81) * 10 + bite - 0x30);
		  }
  
		  
		  
		  var buffer = [gb18030_second, gb18030_third, bite];
  
		  
		  
		  gb18030_first = 0x00;
		  gb18030_second = 0x00;
		  gb18030_third = 0x00;
  
		  
		  
		  if (code_point === null) {
			stream.prepend(buffer);
			return decoderError(fatal);
		  }
  
		  
		  return code_point;
		}
  
		
		if (gb18030_second !== 0x00) {
  
		  
		  
		  if (inRange(bite, 0x81, 0xFE)) {
			gb18030_third = bite;
			return null;
		  }
  
		  
		  
		  stream.prepend([gb18030_second, bite]);
		  gb18030_first = 0x00;
		  gb18030_second = 0x00;
		  return decoderError(fatal);
		}
  
		
		if (gb18030_first !== 0x00) {
  
		  
		  
		  if (inRange(bite, 0x30, 0x39)) {
			gb18030_second = bite;
			return null;
		  }
  
		  
		  
		  var lead = gb18030_first;
		  var pointer = null;
		  gb18030_first = 0x00;
  
		  
		  
		  var offset = bite < 0x7F ? 0x40 : 0x41;
  
		  
		  
		  
		  if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFE))
			pointer = (lead - 0x81) * 190 + (bite - offset);
  
		  
		  
		  code_point = pointer === null ? null :
			  indexCodePointFor(pointer, index('gb18030'));
  
		  
		  
		  if (code_point === null && isASCIIByte(bite))
			stream.prepend(bite);
  
		  
		  if (code_point === null)
			return decoderError(fatal);
  
		  
		  return code_point;
		}
  
		
		
		if (isASCIIByte(bite))
		  return bite;
  
		
		if (bite === 0x80)
		  return 0x20AC;
  
		
		
		if (inRange(bite, 0x81, 0xFE)) {
		  gb18030_first = bite;
		  return null;
		}
  
		
		return decoderError(fatal);
	  };
	}
  
	
	/**
	 * @constructor
	 * @implements {Encoder}
	 * @param {{fatal: boolean}} options
	 * @param {boolean=} gbk_flag
	 */
	function GB18030Encoder(options, gbk_flag) {
	  var fatal = options.fatal;
	  
	  /**
	   * @param {Stream} stream Input stream.
	   * @param {number} code_point Next code point read from the stream.
	   * @return {(number|!Array.<number>)} Byte(s) to emit.
	   */
	  this.handler = function(stream, code_point) {
		
		if (code_point === end_of_stream)
		  return finished;
  
		
		
		if (isASCIICodePoint(code_point))
		  return code_point;
  
		
		if (code_point === 0xE5E5)
		  return encoderError(code_point);
  
		
		
		if (gbk_flag && code_point === 0x20AC)
		  return 0x80;
  
		
		
		var pointer = indexPointerFor(code_point, index('gb18030'));
  
		
		if (pointer !== null) {
  
		  
		  var lead = floor(pointer / 190) + 0x81;
  
		  
		  var trail = pointer % 190;
  
		  
		  var offset = trail < 0x3F ? 0x40 : 0x41;
  
		  
		  return [lead, trail + offset];
		}
  
		
		if (gbk_flag)
		  return encoderError(code_point);
  
		
		
		pointer = indexGB18030RangesPointerFor(code_point);
  
		
		var byte1 = floor(pointer / 10 / 126 / 10);
  
		
		pointer = pointer - byte1 * 10 * 126 * 10;
  
		
		var byte2 = floor(pointer / 10 / 126);
  
		
		pointer = pointer - byte2 * 10 * 126;
  
		
		var byte3 = floor(pointer / 10);
  
		
		var byte4 = pointer - byte3 * 10;
  
		
		
		return [byte1 + 0x81,
				byte2 + 0x30,
				byte3 + 0x81,
				byte4 + 0x30];
	  };
	}
  
	/** @param {{fatal: boolean}} options */
	encoders['gb18030'] = function(options) {
	  return new GB18030Encoder(options);
	};
	/** @param {{fatal: boolean}} options */
	decoders['gb18030'] = function(options) {
	  return new GB18030Decoder(options);
	};
  
  
	
	
	
  
	
  
	
	/**
	 * @constructor
	 * @implements {Decoder}
	 * @param {{fatal: boolean}} options
	 */
	function Big5Decoder(options) {
	  var fatal = options.fatal;
	  
	  var /** @type {number} */ Big5_lead = 0x00;
  
	  /**
	   * @param {Stream} stream The stream of bytes being decoded.
	   * @param {number} bite The next byte read from the stream.
	   * @return {?(number|!Array.<number>)} The next code point(s)
	   *     decoded, or null if not enough data exists in the input
	   *     stream to decode a complete code point.
	   */
	  this.handler = function(stream, bite) {
		
		
		if (bite === end_of_stream && Big5_lead !== 0x00) {
		  Big5_lead = 0x00;
		  return decoderError(fatal);
		}
  
		
		
		if (bite === end_of_stream && Big5_lead === 0x00)
		  return finished;
  
		
		
		
		if (Big5_lead !== 0x00) {
		  var lead = Big5_lead;
		  var pointer = null;
		  Big5_lead = 0x00;
  
		  
		  
		  var offset = bite < 0x7F ? 0x40 : 0x62;
  
		  
		  
		  
		  if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0xA1, 0xFE))
			pointer = (lead - 0x81) * 157 + (bite - offset);
  
		  
		  
		  
		  
		  
		  
		  
		  
		  
		  switch (pointer) {
			case 1133: return [0x00CA, 0x0304];
			case 1135: return [0x00CA, 0x030C];
			case 1164: return [0x00EA, 0x0304];
			case 1166: return [0x00EA, 0x030C];
		  }
  
		  
		  
		  var code_point = (pointer === null) ? null :
			  indexCodePointFor(pointer, index('big5'));
  
		  
		  
		  if (code_point === null && isASCIIByte(bite))
			stream.prepend(bite);
  
		  
		  if (code_point === null)
			return decoderError(fatal);
  
		  
		  return code_point;
		}
  
		
		
		if (isASCIIByte(bite))
		  return bite;
  
		
		
		if (inRange(bite, 0x81, 0xFE)) {
		  Big5_lead = bite;
		  return null;
		}
  
		
		return decoderError(fatal);
	  };
	}
  
	
	/**
	 * @constructor
	 * @implements {Encoder}
	 * @param {{fatal: boolean}} options
	 */
	function Big5Encoder(options) {
	  var fatal = options.fatal;
	  /**
	   * @param {Stream} stream Input stream.
	   * @param {number} code_point Next code point read from the stream.
	   * @return {(number|!Array.<number>)} Byte(s) to emit.
	   */
	  this.handler = function(stream, code_point) {
		
		if (code_point === end_of_stream)
		  return finished;
  
		
		
		if (isASCIICodePoint(code_point))
		  return code_point;
  
		
		var pointer = indexBig5PointerFor(code_point);
  
		
		if (pointer === null)
		  return encoderError(code_point);
  
		
		var lead = floor(pointer / 157) + 0x81;
  
		
		if (lead < 0xA1)
		  return encoderError(code_point);
  
		
		var trail = pointer % 157;
  
		
		
		var offset = trail < 0x3F ? 0x40 : 0x62;
  
		
		return [lead, trail + offset];
	  };
	}
  
	/** @param {{fatal: boolean}} options */
	encoders['Big5'] = function(options) {
	  return new Big5Encoder(options);
	};
	/** @param {{fatal: boolean}} options */
	decoders['Big5'] = function(options) {
	  return new Big5Decoder(options);
	};
  
  
	
	
	
  
	
  
	
	/**
	 * @constructor
	 * @implements {Decoder}
	 * @param {{fatal: boolean}} options
	 */
	function EUCJPDecoder(options) {
	  var fatal = options.fatal;
  
	  
	  
	  var /** @type {boolean} */ eucjp_jis0212_flag = false,
		  /** @type {number} */ eucjp_lead = 0x00;
  
	  /**
	   * @param {Stream} stream The stream of bytes being decoded.
	   * @param {number} bite The next byte read from the stream.
	   * @return {?(number|!Array.<number>)} The next code point(s)
	   *     decoded, or null if not enough data exists in the input
	   *     stream to decode a complete code point.
	   */
	  this.handler = function(stream, bite) {
		
		
		if (bite === end_of_stream && eucjp_lead !== 0x00) {
		  eucjp_lead = 0x00;
		  return decoderError(fatal);
		}
  
		
		
		if (bite === end_of_stream && eucjp_lead === 0x00)
		  return finished;
  
		
		
		
		if (eucjp_lead === 0x8E && inRange(bite, 0xA1, 0xDF)) {
		  eucjp_lead = 0x00;
		  return 0xFF61 - 0xA1 + bite;
		}
  
		
		
		
		if (eucjp_lead === 0x8F && inRange(bite, 0xA1, 0xFE)) {
		  eucjp_jis0212_flag = true;
		  eucjp_lead = bite;
		  return null;
		}
  
		
		
		if (eucjp_lead !== 0x00) {
		  var lead = eucjp_lead;
		  eucjp_lead = 0x00;
  
		  
		  var code_point = null;
  
		  
		  
		  
		  
		  if (inRange(lead, 0xA1, 0xFE) && inRange(bite, 0xA1, 0xFE)) {
			code_point = indexCodePointFor(
			  (lead - 0xA1) * 94 + (bite - 0xA1),
			  index(!eucjp_jis0212_flag ? 'jis0208' : 'jis0212'));
		  }
  
		  
		  eucjp_jis0212_flag = false;
  
		  
		  
		  if (!inRange(bite, 0xA1, 0xFE))
			stream.prepend(bite);
  
		  
		  if (code_point === null)
			return decoderError(fatal);
  
		  
		  return code_point;
		}
  
		
		
		if (isASCIIByte(bite))
		  return bite;
  
		
		
		if (bite === 0x8E || bite === 0x8F || inRange(bite, 0xA1, 0xFE)) {
		  eucjp_lead = bite;
		  return null;
		}
  
		
		return decoderError(fatal);
	  };
	}
  
	
	/**
	 * @constructor
	 * @implements {Encoder}
	 * @param {{fatal: boolean}} options
	 */
	function EUCJPEncoder(options) {
	  var fatal = options.fatal;
	  /**
	   * @param {Stream} stream Input stream.
	   * @param {number} code_point Next code point read from the stream.
	   * @return {(number|!Array.<number>)} Byte(s) to emit.
	   */
	  this.handler = function(stream, code_point) {
		
		if (code_point === end_of_stream)
		  return finished;
  
		
		
		if (isASCIICodePoint(code_point))
		  return code_point;
  
		
		if (code_point === 0x00A5)
		  return 0x5C;
  
		
		if (code_point === 0x203E)
		  return 0x7E;
  
		
		
		
		if (inRange(code_point, 0xFF61, 0xFF9F))
		  return [0x8E, code_point - 0xFF61 + 0xA1];
  
		
		if (code_point === 0x2212)
		  code_point = 0xFF0D;
  
		
		
		var pointer = indexPointerFor(code_point, index('jis0208'));
  
		
		if (pointer === null)
		  return encoderError(code_point);
  
		
		var lead = floor(pointer / 94) + 0xA1;
  
		
		var trail = pointer % 94 + 0xA1;
  
		
		return [lead, trail];
	  };
	}
  
	/** @param {{fatal: boolean}} options */
	encoders['EUC-JP'] = function(options) {
	  return new EUCJPEncoder(options);
	};
	/** @param {{fatal: boolean}} options */
	decoders['EUC-JP'] = function(options) {
	  return new EUCJPDecoder(options);
	};
  
	
  
	
	/**
	 * @constructor
	 * @implements {Decoder}
	 * @param {{fatal: boolean}} options
	 */
	function ISO2022JPDecoder(options) {
	  var fatal = options.fatal;
	  /** @enum */
	  var states = {
		ASCII: 0,
		Roman: 1,
		Katakana: 2,
		LeadByte: 3,
		TrailByte: 4,
		EscapeStart: 5,
		Escape: 6
	  };
	  
	  
	  
	  
	  var /** @type {number} */ iso2022jp_decoder_state = states.ASCII,
		  /** @type {number} */ iso2022jp_decoder_output_state = states.ASCII,
		  /** @type {number} */ iso2022jp_lead = 0x00,
		  /** @type {boolean} */ iso2022jp_output_flag = false;
	  /**
	   * @param {Stream} stream The stream of bytes being decoded.
	   * @param {number} bite The next byte read from the stream.
	   * @return {?(number|!Array.<number>)} The next code point(s)
	   *     decoded, or null if not enough data exists in the input
	   *     stream to decode a complete code point.
	   */
	  this.handler = function(stream, bite) {
		
		switch (iso2022jp_decoder_state) {
		default:
		case states.ASCII:
		  
		  
  
		  
		  if (bite === 0x1B) {
			
			
			iso2022jp_decoder_state = states.EscapeStart;
			return null;
		  }
  
		  
		  if (inRange(bite, 0x00, 0x7F) && bite !== 0x0E
			  && bite !== 0x0F && bite !== 0x1B) {
			
			
			iso2022jp_output_flag = false;
			return bite;
		  }
  
		  
		  if (bite === end_of_stream) {
			
			return finished;
		  }
  
		  
		  
		  iso2022jp_output_flag = false;
		  return decoderError(fatal);
  
		case states.Roman:
		  
		  
  
		  
		  if (bite === 0x1B) {
			
			
			iso2022jp_decoder_state = states.EscapeStart;
			return null;
		  }
  
		  
		  if (bite === 0x5C) {
			
			
			iso2022jp_output_flag = false;
			return 0x00A5;
		  }
  
		  
		  if (bite === 0x7E) {
			
			
			iso2022jp_output_flag = false;
			return 0x203E;
		  }
  
		  
		  if (inRange(bite, 0x00, 0x7F) && bite !== 0x0E && bite !== 0x0F
			  && bite !== 0x1B && bite !== 0x5C && bite !== 0x7E) {
			
			
			iso2022jp_output_flag = false;
			return bite;
		  }
  
		  
		  if (bite === end_of_stream) {
			
			return finished;
		  }
  
		  
		  
		  iso2022jp_output_flag = false;
		  return decoderError(fatal);
  
		case states.Katakana:
		  
		  
  
		  
		  if (bite === 0x1B) {
			
			
			iso2022jp_decoder_state = states.EscapeStart;
			return null;
		  }
  
		  
		  if (inRange(bite, 0x21, 0x5F)) {
			
			
			iso2022jp_output_flag = false;
			return 0xFF61 - 0x21 + bite;
		  }
  
		  
		  if (bite === end_of_stream) {
			
			return finished;
		  }
  
		  
		  
		  iso2022jp_output_flag = false;
		  return decoderError(fatal);
  
		case states.LeadByte:
		  
		  
  
		  
		  if (bite === 0x1B) {
			
			
			iso2022jp_decoder_state = states.EscapeStart;
			return null;
		  }
  
		  
		  if (inRange(bite, 0x21, 0x7E)) {
			
			
			
			iso2022jp_output_flag = false;
			iso2022jp_lead = bite;
			iso2022jp_decoder_state = states.TrailByte;
			return null;
		  }
  
		  
		  if (bite === end_of_stream) {
			
			return finished;
		  }
  
		  
		  
		  iso2022jp_output_flag = false;
		  return decoderError(fatal);
  
		case states.TrailByte:
		  
		  
  
		  
		  if (bite === 0x1B) {
			
			
			iso2022jp_decoder_state = states.EscapeStart;
			return decoderError(fatal);
		  }
  
		  
		  if (inRange(bite, 0x21, 0x7E)) {
			
			iso2022jp_decoder_state = states.LeadByte;
  
			
			var pointer = (iso2022jp_lead - 0x21) * 94 + bite - 0x21;
  
			
			
			var code_point = indexCodePointFor(pointer, index('jis0208'));
  
			
			if (code_point === null)
			  return decoderError(fatal);
  
			
			return code_point;
		  }
  
		  
		  if (bite === end_of_stream) {
			
			
			iso2022jp_decoder_state = states.LeadByte;
			stream.prepend(bite);
			return decoderError(fatal);
		  }
  
		  
		  
		  
		  iso2022jp_decoder_state = states.LeadByte;
		  return decoderError(fatal);
  
		case states.EscapeStart:
		  
  
		  
		  
		  
		  if (bite === 0x24 || bite === 0x28) {
			iso2022jp_lead = bite;
			iso2022jp_decoder_state = states.Escape;
			return null;
		  }
  
		  
		  stream.prepend(bite);
  
		  
		  
		  
		  iso2022jp_output_flag = false;
		  iso2022jp_decoder_state = iso2022jp_decoder_output_state;
		  return decoderError(fatal);
  
		case states.Escape:
		  
  
		  
		  
		  var lead = iso2022jp_lead;
		  iso2022jp_lead = 0x00;
  
		  
		  var state = null;
  
		  
		  if (lead === 0x28 && bite === 0x42)
			state = states.ASCII;
  
		  
		  if (lead === 0x28 && bite === 0x4A)
			state = states.Roman;
  
		  
		  if (lead === 0x28 && bite === 0x49)
			state = states.Katakana;
  
		  
		  
		  if (lead === 0x24 && (bite === 0x40 || bite === 0x42))
			state = states.LeadByte;
  
		  
		  if (state !== null) {
			
			
			iso2022jp_decoder_state = iso2022jp_decoder_state = state;
  
			
			var output_flag = iso2022jp_output_flag;
  
			
			iso2022jp_output_flag = true;
  
			
			
			return !output_flag ? null : decoderError(fatal);
		  }
  
		  
		  stream.prepend([lead, bite]);
  
		  
		  
		  
		  iso2022jp_output_flag = false;
		  iso2022jp_decoder_state = iso2022jp_decoder_output_state;
		  return decoderError(fatal);
		}
	  };
	}
  
	
	/**
	 * @constructor
	 * @implements {Encoder}
	 * @param {{fatal: boolean}} options
	 */
	function ISO2022JPEncoder(options) {
	  var fatal = options.fatal;
	  
	  
	  
	  /** @enum */
	  var states = {
		ASCII: 0,
		Roman: 1,
		jis0208: 2
	  };
	  var /** @type {number} */ iso2022jp_state = states.ASCII;
	  /**
	   * @param {Stream} stream Input stream.
	   * @param {number} code_point Next code point read from the stream.
	   * @return {(number|!Array.<number>)} Byte(s) to emit.
	   */
	  this.handler = function(stream, code_point) {
		
		
		
		
		if (code_point === end_of_stream &&
			iso2022jp_state !== states.ASCII) {
		  stream.prepend(code_point);
		  iso2022jp_state = states.ASCII;
		  return [0x1B, 0x28, 0x42];
		}
  
		
		
		if (code_point === end_of_stream && iso2022jp_state === states.ASCII)
		  return finished;
  
		
		
		if ((iso2022jp_state === states.ASCII ||
			 iso2022jp_state === states.Roman) &&
			(code_point === 0x000E || code_point === 0x000F ||
			 code_point === 0x001B)) {
		  return encoderError(0xFFFD);
		}
  
		
		
		if (iso2022jp_state === states.ASCII &&
			isASCIICodePoint(code_point))
		  return code_point;
  
		
		
		
		if (iso2022jp_state === states.Roman &&
			((isASCIICodePoint(code_point) &&
			 code_point !== 0x005C && code_point !== 0x007E) ||
			(code_point == 0x00A5 || code_point == 0x203E))) {
  
		  
		  
		  if (isASCIICodePoint(code_point))
			return code_point;
  
		  
		  if (code_point === 0x00A5)
			return 0x5C;
  
		  
		  if (code_point === 0x203E)
			return 0x7E;
		}
  
		
		
		
		
		if (isASCIICodePoint(code_point) &&
			iso2022jp_state !== states.ASCII) {
		  stream.prepend(code_point);
		  iso2022jp_state = states.ASCII;
		  return [0x1B, 0x28, 0x42];
		}
  
		
		
		
		
		if ((code_point === 0x00A5 || code_point === 0x203E) &&
			iso2022jp_state !== states.Roman) {
		  stream.prepend(code_point);
		  iso2022jp_state = states.Roman;
		  return [0x1B, 0x28, 0x4A];
		}
  
		
		if (code_point === 0x2212)
		  code_point = 0xFF0D;
  
		
		
		var pointer = indexPointerFor(code_point, index('jis0208'));
  
		
		if (pointer === null)
		  return encoderError(code_point);
  
		
		
		
		if (iso2022jp_state !== states.jis0208) {
		  stream.prepend(code_point);
		  iso2022jp_state = states.jis0208;
		  return [0x1B, 0x24, 0x42];
		}
  
		
		var lead = floor(pointer / 94) + 0x21;
  
		
		var trail = pointer % 94 + 0x21;
  
		
		return [lead, trail];
	  };
	}
  
	/** @param {{fatal: boolean}} options */
	encoders['ISO-2022-JP'] = function(options) {
	  return new ISO2022JPEncoder(options);
	};
	/** @param {{fatal: boolean}} options */
	decoders['ISO-2022-JP'] = function(options) {
	  return new ISO2022JPDecoder(options);
	};
  
	
  
	
	/**
	 * @constructor
	 * @implements {Decoder}
	 * @param {{fatal: boolean}} options
	 */
	function ShiftJISDecoder(options) {
	  var fatal = options.fatal;
	  
	  
	  var /** @type {number} */ Shift_JIS_lead = 0x00;
	  /**
	   * @param {Stream} stream The stream of bytes being decoded.
	   * @param {number} bite The next byte read from the stream.
	   * @return {?(number|!Array.<number>)} The next code point(s)
	   *     decoded, or null if not enough data exists in the input
	   *     stream to decode a complete code point.
	   */
	  this.handler = function(stream, bite) {
		
		
		if (bite === end_of_stream && Shift_JIS_lead !== 0x00) {
		  Shift_JIS_lead = 0x00;
		  return decoderError(fatal);
		}
  
		
		
		if (bite === end_of_stream && Shift_JIS_lead === 0x00)
		  return finished;
  
		
		
		
		if (Shift_JIS_lead !== 0x00) {
		  var lead = Shift_JIS_lead;
		  var pointer = null;
		  Shift_JIS_lead = 0x00;
  
		  
		  
		  var offset = (bite < 0x7F) ? 0x40 : 0x41;
  
		  
		  
		  var lead_offset = (lead < 0xA0) ? 0x81 : 0xC1;
  
		  
		  
		  
		  if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFC))
			pointer = (lead - lead_offset) * 188 + bite - offset;
  
		  
		  
		  if (inRange(pointer, 8836, 10715))
			return 0xE000 - 8836 + pointer;
  
		  
		  
		  var code_point = (pointer === null) ? null :
				indexCodePointFor(pointer, index('jis0208'));
  
		  
		  
		  if (code_point === null && isASCIIByte(bite))
			stream.prepend(bite);
  
		  
		  if (code_point === null)
			return decoderError(fatal);
  
		  
		  return code_point;
		}
  
		
		
		if (isASCIIByte(bite) || bite === 0x80)
		  return bite;
  
		
		
		if (inRange(bite, 0xA1, 0xDF))
		  return 0xFF61 - 0xA1 + bite;
  
		
		
		
		if (inRange(bite, 0x81, 0x9F) || inRange(bite, 0xE0, 0xFC)) {
		  Shift_JIS_lead = bite;
		  return null;
		}
  
		
		return decoderError(fatal);
	  };
	}
  
	
	/**
	 * @constructor
	 * @implements {Encoder}
	 * @param {{fatal: boolean}} options
	 */
	function ShiftJISEncoder(options) {
	  var fatal = options.fatal;
	  /**
	   * @param {Stream} stream Input stream.
	   * @param {number} code_point Next code point read from the stream.
	   * @return {(number|!Array.<number>)} Byte(s) to emit.
	   */
	  this.handler = function(stream, code_point) {
		
		if (code_point === end_of_stream)
		  return finished;
  
		
		
		if (isASCIICodePoint(code_point) || code_point === 0x0080)
		  return code_point;
  
		
		if (code_point === 0x00A5)
		  return 0x5C;
  
		
		if (code_point === 0x203E)
		  return 0x7E;
  
		
		
		if (inRange(code_point, 0xFF61, 0xFF9F))
		  return code_point - 0xFF61 + 0xA1;
  
		
		if (code_point === 0x2212)
		  code_point = 0xFF0D;
  
		
		var pointer = indexShiftJISPointerFor(code_point);
  
		
		if (pointer === null)
		  return encoderError(code_point);
  
		
		var lead = floor(pointer / 188);
  
		
		
		var lead_offset = (lead < 0x1F) ? 0x81 : 0xC1;
  
		
		var trail = pointer % 188;
  
		
		
		var offset = (trail < 0x3F) ? 0x40 : 0x41;
  
		
		
		return [lead + lead_offset, trail + offset];
	  };
	}
  
	/** @param {{fatal: boolean}} options */
	encoders['Shift_JIS'] = function(options) {
	  return new ShiftJISEncoder(options);
	};
	/** @param {{fatal: boolean}} options */
	decoders['Shift_JIS'] = function(options) {
	  return new ShiftJISDecoder(options);
	};
  
	
	
	
  
	
  
	
	/**
	 * @constructor
	 * @implements {Decoder}
	 * @param {{fatal: boolean}} options
	 */
	function EUCKRDecoder(options) {
	  var fatal = options.fatal;
  
	  
	  var /** @type {number} */ euckr_lead = 0x00;
	  /**
	   * @param {Stream} stream The stream of bytes being decoded.
	   * @param {number} bite The next byte read from the stream.
	   * @return {?(number|!Array.<number>)} The next code point(s)
	   *     decoded, or null if not enough data exists in the input
	   *     stream to decode a complete code point.
	   */
	  this.handler = function(stream, bite) {
		
		
		if (bite === end_of_stream && euckr_lead !== 0) {
		  euckr_lead = 0x00;
		  return decoderError(fatal);
		}
  
		
		
		if (bite === end_of_stream && euckr_lead === 0)
		  return finished;
  
		
		
		
		if (euckr_lead !== 0x00) {
		  var lead = euckr_lead;
		  var pointer = null;
		  euckr_lead = 0x00;
  
		  
		  
		  if (inRange(bite, 0x41, 0xFE))
			pointer = (lead - 0x81) * 190 + (bite - 0x41);
  
		  
		  
		  var code_point = (pointer === null)
				? null : indexCodePointFor(pointer, index('euc-kr'));
  
		  
		  
		  if (pointer === null && isASCIIByte(bite))
			stream.prepend(bite);
  
		  
		  if (code_point === null)
			return decoderError(fatal);
  
		  
		  return code_point;
		}
  
		
		
		if (isASCIIByte(bite))
		  return bite;
  
		
		
		if (inRange(bite, 0x81, 0xFE)) {
		  euckr_lead = bite;
		  return null;
		}
  
		
		return decoderError(fatal);
	  };
	}
  
	
	/**
	 * @constructor
	 * @implements {Encoder}
	 * @param {{fatal: boolean}} options
	 */
	function EUCKREncoder(options) {
	  var fatal = options.fatal;
	  /**
	   * @param {Stream} stream Input stream.
	   * @param {number} code_point Next code point read from the stream.
	   * @return {(number|!Array.<number>)} Byte(s) to emit.
	   */
	  this.handler = function(stream, code_point) {
		
		if (code_point === end_of_stream)
		  return finished;
  
		
		
		if (isASCIICodePoint(code_point))
		  return code_point;
  
		
		
		var pointer = indexPointerFor(code_point, index('euc-kr'));
  
		
		if (pointer === null)
		  return encoderError(code_point);
  
		
		var lead = floor(pointer / 190) + 0x81;
  
		
		var trail = (pointer % 190) + 0x41;
  
		
		return [lead, trail];
	  };
	}
  
	/** @param {{fatal: boolean}} options */
	encoders['EUC-KR'] = function(options) {
	  return new EUCKREncoder(options);
	};
	/** @param {{fatal: boolean}} options */
	decoders['EUC-KR'] = function(options) {
	  return new EUCKRDecoder(options);
	};
  
  
	
	
	
  
	
  
	
  
	
  
	/**
	 * @param {number} code_unit
	 * @param {boolean} utf16be
	 * @return {!Array.<number>} bytes
	 */
	function convertCodeUnitToBytes(code_unit, utf16be) {
	  
	  var byte1 = code_unit >> 8;
  
	  
	  var byte2 = code_unit & 0x00FF;
  
	  
		  
	  if (utf16be)
		return [byte1, byte2];
	  
	  return [byte2, byte1];
	}
  
	
	/**
	 * @constructor
	 * @implements {Decoder}
	 * @param {boolean} utf16_be True if big-endian, false if little-endian.
	 * @param {{fatal: boolean}} options
	 */
	function UTF16Decoder(utf16_be, options) {
	  var fatal = options.fatal;
	  var /** @type {?number} */ utf16_lead_byte = null,
		  /** @type {?number} */ utf16_lead_surrogate = null;
	  /**
	   * @param {Stream} stream The stream of bytes being decoded.
	   * @param {number} bite The next byte read from the stream.
	   * @return {?(number|!Array.<number>)} The next code point(s)
	   *     decoded, or null if not enough data exists in the input
	   *     stream to decode a complete code point.
	   */
	  this.handler = function(stream, bite) {
		
		
		
		if (bite === end_of_stream && (utf16_lead_byte !== null ||
								  utf16_lead_surrogate !== null)) {
		  return decoderError(fatal);
		}
  
		
		
		if (bite === end_of_stream && utf16_lead_byte === null &&
			utf16_lead_surrogate === null) {
		  return finished;
		}
  
		
		
		if (utf16_lead_byte === null) {
		  utf16_lead_byte = bite;
		  return null;
		}
  
		
		var code_unit;
		if (utf16_be) {
		  
		  
		  code_unit = (utf16_lead_byte << 8) + bite;
		} else {
		  
		  
		  code_unit = (bite << 8) + utf16_lead_byte;
		}
		
		utf16_lead_byte = null;
  
		
		
		
		if (utf16_lead_surrogate !== null) {
		  var lead_surrogate = utf16_lead_surrogate;
		  utf16_lead_surrogate = null;
  
		  
		  
		  
		  if (inRange(code_unit, 0xDC00, 0xDFFF)) {
			return 0x10000 + (lead_surrogate - 0xD800) * 0x400 +
				(code_unit - 0xDC00);
		  }
  
		  
		  
		  
		  stream.prepend(convertCodeUnitToBytes(code_unit, utf16_be));
		  return decoderError(fatal);
		}
  
		
		
		if (inRange(code_unit, 0xD800, 0xDBFF)) {
		  utf16_lead_surrogate = code_unit;
		  return null;
		}
  
		
		
		if (inRange(code_unit, 0xDC00, 0xDFFF))
		  return decoderError(fatal);
  
		
		return code_unit;
	  };
	}
  
	
	/**
	 * @constructor
	 * @implements {Encoder}
	 * @param {boolean} utf16_be True if big-endian, false if little-endian.
	 * @param {{fatal: boolean}} options
	 */
	function UTF16Encoder(utf16_be, options) {
	  var fatal = options.fatal;
	  /**
	   * @param {Stream} stream Input stream.
	   * @param {number} code_point Next code point read from the stream.
	   * @return {(number|!Array.<number>)} Byte(s) to emit.
	   */
	  this.handler = function(stream, code_point) {
		
		if (code_point === end_of_stream)
		  return finished;
  
		
		
		
		if (inRange(code_point, 0x0000, 0xFFFF))
		  return convertCodeUnitToBytes(code_point, utf16_be);
  
		
		
		var lead = convertCodeUnitToBytes(
		  ((code_point - 0x10000) >> 10) + 0xD800, utf16_be);
  
		
		
		var trail = convertCodeUnitToBytes(
		  ((code_point - 0x10000) & 0x3FF) + 0xDC00, utf16_be);
  
		
		return lead.concat(trail);
	  };
	}
  
	
	
	/** @param {{fatal: boolean}} options */
	encoders['UTF-16BE'] = function(options) {
	  return new UTF16Encoder(true, options);
	};
	
	/** @param {{fatal: boolean}} options */
	decoders['UTF-16BE'] = function(options) {
	  return new UTF16Decoder(true, options);
	};
  
	
	
	/** @param {{fatal: boolean}} options */
	encoders['UTF-16LE'] = function(options) {
	  return new UTF16Encoder(false, options);
	};
	
	/** @param {{fatal: boolean}} options */
	decoders['UTF-16LE'] = function(options) {
	  return new UTF16Decoder(false, options);
	};
  
  
	function XUserDefinedDecoder(options) {
	  var fatal = options.fatal;
  
	  this.handler = function(stream, bite) {
		
		if (bite === end_of_stream)
		  return finished;
  
		
		
		if (isASCIIByte(bite))
		  return bite;
  
		
		return 0xF780 + bite - 0x80;
	  };
	}
  
  
	function XUserDefinedEncoder(options) {
	  var fatal = options.fatal;
  
	  this.handler = function(stream, code_point) {
		
		if (code_point === end_of_stream)
		  return finished;
  
		
		
		if (isASCIICodePoint(code_point))
		  return code_point;
  
		
		
		if (inRange(code_point, 0xF780, 0xF7FF))
		  return code_point - 0xF780 + 0x80;
  
		
		return encoderError(code_point);
	  };
	}
  
	/** @param {{fatal: boolean}} options */
	encoders['x-user-defined'] = function(options) {
	  return new XUserDefinedEncoder(options);
	};
	/** @param {{fatal: boolean}} options */
	decoders['x-user-defined'] = function(options) {
	  return new XUserDefinedDecoder(options);
	};
  
	if (!global['TextEncoder'])
	  global['TextEncoder'] = TextEncoder;
	if (!global['TextDecoder'])
	  global['TextDecoder'] = TextDecoder;
  
	if (typeof module !== "undefined" && module.exports) {
	  module.exports = {
		TextEncoder: global['TextEncoder'],
		TextDecoder: global['TextDecoder'],
		EncodingIndexes: global["encoding-indexes"]
	  };
	}
  
  
  
  }(this || {}));